package com.rw.tool.util.expression.expression.parser;

import com.rw.tool.util.expression.element.ExpressionElement;
import com.rw.tool.util.expression.element.LeftParentheses;
import com.rw.tool.util.expression.element.RightParentheses;
import com.rw.tool.util.expression.exception.InfixExpressionParseException;
import com.rw.tool.util.expression.exception.ParenthesesNotMatchException;
import com.rw.tool.util.expression.expression.InfixExpression;
import com.rw.tool.util.expression.expression.parser.element.*;
import com.rw.tool.util.expression.state.InfixExpressionState;
import com.rw.tool.util.string.StringScanner;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;

/**
 * 中缀表达式解析器
 */
public class InfixExpressionParser {
    /**
     * 解析字符串，使其变为合法的表达式列表
     * 如果字符串不符合中缀表达式结构，则会抛出异常
     * scale代表精确到小数点后几位
     */
    public InfixExpression parse(String expression, int scale) {
        // 设置初始状态。第一个元素必须是数字或者左括号
        InfixExpressionState state = InfixExpressionState.START;

        // 扫描字符串
        StringScanner scanner = new StringScanner(expression.replace(" ", ""));
        List<ExpressionElement> result = new ArrayList<>();
        int parenthesesCount = 0;
        while (scanner.hasNext()) {
            Optional<ExpressionElement> elementOptional = search(state, scanner, scale);
            // 如果扫描失败
            if (!elementOptional.isPresent()) {
                throw new InfixExpressionParseException(state, expression, scanner.getIndex());
            }
            ExpressionElement element = elementOptional.get();
            state = InfixExpressionState.of(element);
            result.add(element);

            if (element == LeftParentheses.LEFT) {
                // 如果是左括号，计数+1
                parenthesesCount++;
            } else if (element == RightParentheses.RIGHT) {
                // 如果是右括号，计数-1
                parenthesesCount--;
            }
        }

        if (state != InfixExpressionState.AFTER_NUMBER && state != InfixExpressionState.AFTER_RIGHT_PARENTHESES) {
            throw new InfixExpressionParseException(state, expression, scanner.getIndex());
        }

        // 如果计数不等于0，说明有未匹配的括号
        if (parenthesesCount != 0) {
            throw new ParenthesesNotMatchException("There are unmatched parentheses in the expression!");
        }

        // 扫描完毕，返回结果
        return new InfixExpression(result);
    }

    /**
     * 根据状态，往下解析字符串
     *
     * @param state   当前状态
     * @param scanner 字符串扫描器
     * @param scale   精确位数
     * @return 解析出的元素
     */
    protected Optional<ExpressionElement> search(InfixExpressionState state, StringScanner scanner, int scale) {
        // 获取当前状态，能够紧跟在后的表达式元素
        Set<Class<? extends ExpressionElement>> canBeFollowed = state.getCanBeFollowed();

        // 获取元素解析器
        List<ElementParser<?>> elementParser = getElementParser();
        // 遍历这些元素
        for (Class<? extends ExpressionElement> element : canBeFollowed) {
            // 遍历每个元素解析器
            for (ElementParser<?> parser : elementParser) {
                // 如果找到了对应的解析器
                if (parser.trigger(element)) {
                    // 进行解析
                    Optional<ExpressionElement> result = parser.handler(scanner, scale);
                    // 如果解析成功，则把结果进行返回
                    if (result.isPresent()) {
                        return result;
                    }
                }
            }
        }

        // 扫描失败
        return Optional.empty();
    }

    /**
     * 获取表达式元素解析器
     *
     * @return 返回表达式元素列表
     */
    protected List<ElementParser<?>> getElementParser() {
        List<ElementParser<?>> list = new ArrayList<>(4);

        list.add(new NumberElementParser());
        list.add(new CalculationElementParser());
        list.add(new LeftParenthesesElementParser());
        list.add(new RightParenthesesElementParser());

        return list;
    }
}
